package cn.yx.common.security.webflux.repository;

import cn.hutool.core.util.StrUtil;
import cn.yx.common.core.util.StringUtil;
import cn.yx.common.security.pojo.JwtCacheToken;
import cn.yx.common.security.pojo.JwtUserDetails;
import cn.yx.common.security.properties.SecurityProperties;
import cn.yx.common.security.service.JwtTokenService;
import cn.yx.common.security.util.JwtUtil;
import cn.yx.common.security.webflux.constant.message.JwtAuthenticationFilterMessageKey;
import cn.yx.common.security.webflux.manager.JwtTokenAuthenticationManager;
import cn.yx.common.webflux.util.WebFluxUtil;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * <p>自定义的SecurityContextRepository</p>
 *
 * @author Wgssmart
 */
@Component
public class CustomSecurityContextRepository implements ServerSecurityContextRepository {

    private final SecurityProperties securityProperties;

    private final JwtTokenService jwtTokenService;

    @Resource
    private JwtTokenAuthenticationManager jwtTokenAuthenticationManager;

    public CustomSecurityContextRepository(SecurityProperties securityProperties, JwtTokenService jwtTokenService) {
        this.securityProperties = securityProperties;
        this.jwtTokenService = jwtTokenService;
    }

    @Override
    public Mono<Void> save(ServerWebExchange exchange, SecurityContext context) {
        return Mono.empty();
    }

    @Override
    public Mono<SecurityContext> load(ServerWebExchange exchange) {
        String token = getJwtToken(exchange.getRequest());
        if (StrUtil.isNotEmpty(token)) {
            JwtUserDetails jwtUserDetails = JwtUtil.parseJwtUserDetails(token);
            if (Objects.isNull(jwtUserDetails)) {
                WebFluxUtil.write(exchange.getResponse(), JwtAuthenticationFilterMessageKey.TOKEN_ERROR);
                return Mono.empty();
            }
            // 由token解析出JwtUserDetails
            JwtCacheToken jwtCacheToken = jwtTokenService.getCachedToken(jwtUserDetails);
            if (Objects.isNull(jwtCacheToken)) {
                WebFluxUtil.write(exchange.getResponse(), JwtAuthenticationFilterMessageKey.TOKEN_NOT_EXIST);
                return Mono.empty();
            }
            String tokenInRedis = jwtCacheToken.getToken();
            if (!StrUtil.equals(token, tokenInRedis)) {
                WebFluxUtil.write(exchange.getResponse(), JwtAuthenticationFilterMessageKey.TOKEN_ERROR);
                return Mono.empty();
            }
            // 只要jwt token正常使用了，就刷新缓存失效时间，注意jwt token本身的过期时间并未变更
            jwtTokenService.refreshTokenExpiration(jwtUserDetails);
            // 如果redis中存在此token
            return jwtTokenAuthenticationManager.authenticate(new UsernamePasswordAuthenticationToken(
                    jwtUserDetails, null, jwtUserDetails.getAuthorities())).map(SecurityContextImpl::new);
        }
        return Mono.empty();
    }

    private String getJwtToken(ServerHttpRequest request) {
        String token = WebFluxUtil.getHeader(request, securityProperties.getJwtToken().getHeader());
        if (StrUtil.isNotEmpty(token) && token.startsWith(securityProperties.getJwtToken().getHeaderPrefix())) {
            return token.replace(securityProperties.getJwtToken().getHeaderPrefix(), StringUtil.EMPTY_STRING);
        }
        return null;
    }

}